﻿//---------------------------------------------------------------------------
// 
// File: HtmlTokenType.cs
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
// Description: Definition of token types supported by HtmlLexicalAnalyzer
//
//---------------------------------------------------------------------------

namespace HTMLConverter
{
    using System.Collections.Generic;
    using System.Linq;
    using HTMLConverter.Xml;
    using System.Xml.Linq;

    /// <summary>
    /// HtmlToXamlConverter is a static class that takes an HTML string
    /// and converts it into XAML
    /// </summary>
    public static class HtmlConverter
    {
        #region const values.

        internal const string Xaml_Namespace = "http://schemas.microsoft.com/winfx/2006/xaml/presentation";

        internal const string Xaml_Document = "StackPanel";
        internal const string Xaml_RichTextBox = "RichTextBox";

        internal const string Xaml_Section = "Section";

        internal const string Xaml_Paragraph = "Paragraph";

        internal const string Xaml_Run = "Run";
        internal const string Xaml_Span = "Span";

        internal const string Xaml_LineBreak = "LineBreak";

        internal const string Xaml_Hyperlink = "Hyperlink";
        internal const string Xaml_Hyperlink_NavigateUri = "NavigateUri";
        internal const string Xaml_Hyperlink_TargetName = "TargetName";
        internal const string Xaml_Hyperlink_TargetName_Blank = "_blank";
        internal const string Xaml_Hyperlink_Foreground_Color = "Blue";
        internal const string Xaml_Hyperlink_MouseOverForeground = "MouseOverForeground";
        internal const string Xaml_Hyperlink_MouseOverForeground_Color = "{StaticResource PhoneSubtleBrush}";

        internal const string Xaml_InlineUIContainer = "InlineUIContainer";
        internal const string Xaml_Image = "Image";
        internal const string Xaml_Image_Source = "Source";
        internal const string Xaml_Image_Tag = "Tag";

        internal const string Xaml_Width = "Width";
        internal const string Xaml_Brushes_Black = "Black";

        internal const string Xaml_FontSize = "FontSize";
        internal const string Xaml_FontSize_XXLarge = "48"; // "XXLarge";
        internal const string Xaml_FontSize_XLarge = "32"; // "XLarge";
        internal const string Xaml_FontSize_Large = "30"; // "Large";
        internal const string Xaml_FontSize_Medium = "26"; // "Medium";
        internal const string Xaml_FontSize_Small = "24"; // "Small";
        internal const string Xaml_FontSize_XSmall = "22"; // "XSmall";

        internal const string Xaml_FontWeight = "FontWeight";
        internal const string Xaml_FontWeight_Bold = "Bold";

        internal const string Xaml_FontStyle = "FontStyle";
        internal const string Xaml_FontStyle_Italic = "Italic";

        internal const string Xaml_Foreground = "Foreground";
        internal const string Xaml_Background = "Background";
        internal const string Xaml_TextDecorations = "TextDecorations";
        internal const string Xaml_TextDecorations_Underline = "Underline";

        internal const string Xaml_Text = "Text";

        #endregion

        public static string HtmlToXaml(string htmlString)
        {
            // Create well-formed Xml from Html string
            XmlElement htmlElement = HtmlParser.ParseHtml(htmlString);

            // Create an xamlDocumentElement for generated xaml
            return DocumentWrapper(Convert(htmlElement)).OuterXml;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="htmlElement"></param>
        /// <returns></returns>
        private static XNode[] Convert(XNode htmlNode)
        {
            List<XNode> nodes = new List<XNode>();
            XmlElement htmlElement = htmlNode as XmlElement;
            if (htmlElement != null)
            {
                // Switch to an appropriate kind of processing depending on html element name
                switch (htmlElement.LowerName)
                {
                    case "html":
                    case "body":
                    case "form":
                    case "pre":
                    case "blockquote":
                    case "div":
                        // unskin
                        for (XNode htmlChildNode = htmlElement.FirstNode; htmlChildNode != null; htmlChildNode = htmlChildNode.NextNode)
                        {
                            nodes.AddRange(Convert(htmlChildNode));
                        }
                        break;

                    case "p":
                    case "textarea":
                        nodes.Add(Converters.Collection[Tag.Paragraph].Convert(htmlElement));
                        break;

                    case "a":
                        nodes.Add(Converters.Collection[Tag.Hyperlink].Convert(htmlElement));
                        break;

                    case "img":
                        nodes.Add(Converters.Collection[Tag.Image].Convert(htmlElement));
                        break;

                    case "h1":
                    case "h2":
                    case "h3":
                    case "h4":
                    case "h5":
                    case "h6":
                        nodes.Add(Converters.Collection[Tag.Title].Convert(htmlElement));
                        break;

                    case "b":
                    case "strong":
                    case "em":
                    case "dfn":
                    case "code":
                    case "samp":
                    case "var":
                    case "cite":
                    case "kbd":
                        nodes.Add(Converters.Collection[Tag.Bold].Convert(htmlElement));
                        break;

                    case "font":
                        nodes.Add(Converters.Collection[Tag.Run].Convert(htmlElement));
                        break;

                    case "ol":
                    case "ul":
                        // List element conversion
                        nodes.Add(Converters.Collection[Tag.List].Convert(htmlElement));
                        break;

                    case "table":
                        nodes.Add(Converters.Collection[Tag.Table].Convert(htmlElement));
                        break;

                    default:
                        nodes.Add(Converters.Collection[Tag.Span].Convert(htmlElement));
                        break;
                }
            }
            else if (htmlNode is XmlText)
            {
                XmlText text = htmlNode as XmlText;
                if (string.IsNullOrEmpty(text.Value.Trim()) == false)
                {
                    nodes.Add(HtmlConverter.CteateText(text.Value));
                }
            }


            return nodes.ToArray();
        }

        /// <summary>
        /// 将所有的Node包装成RichTextBox
        /// </summary>
        /// <param name="nodes"></param>
        /// <returns></returns>
        private static XmlElement DocumentWrapper(XNode[] nodes)
        {
            bool flag = false;
            XmlElement document = new XmlElement("StackPanel", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
            XmlElement wrapper = null;
            foreach (XNode xamlChildNode in nodes)
            {
                XmlElement e = xamlChildNode as XmlElement;
                flag = e != null && e.LowerName == "span";
                if (flag)
                {
                    var el = e.LastNode as XmlElement;
                    flag = el != null && el.LowerName == "linebreak";
                }

                if (flag)
                {
                    if (wrapper != null)
                    {
                        document.AppendChild(wrapper);
                        wrapper = null;
                    }
                    document.AppendChild(HtmlConverter.ElementWrapper(xamlChildNode));
                }
                else if (wrapper != null)
                {
                    (wrapper.FirstChild as XmlElement).AppendChild(xamlChildNode);
                }
                else
                {
                    wrapper = HtmlConverter.ElementWrapper(xamlChildNode);
                }
            }

            if (wrapper != null)
            {
                document.AppendChild(wrapper);
            }

            return document;
        }

        static XmlElement ElementWrapper(XNode xamlNode)
        {
            XmlElement element = xamlNode as XmlElement;
            if ((element != null && element.FirstNode != null) || (element != null && element.LocalName == "Run") || xamlNode is XmlText)
            {
                XmlElement xamlRichTextBox = new XmlElement(Xaml_RichTextBox, Xaml_Namespace);
                XmlElement xamlParagraph = new XmlElement(Xaml_Paragraph, Xaml_Namespace);

                xamlParagraph.AppendChild(xamlNode);
                xamlRichTextBox.AppendChild(xamlParagraph);

                return xamlRichTextBox;
            }

            return null;
        }

        internal static XNode CteateText(string text)
        {
            return new XmlText(text);
        }

        internal static XNode CteateLineBreak()
        {
            return new XmlElement(HtmlConverter.Xaml_LineBreak, HtmlConverter.Xaml_Namespace);
        }
    }
}
